<?php


namespace TDEngineOrm;
use TDEngineOrm\TDEngine;
use TDEngineOrm\Connection;


class Query
{
    // 数据库Connection对象实例
    protected $connection;
    // 数据库Builder对象实例
    protected $builder;
    // 当前数据表名称（不含前缀）
    protected $name = '';
    // 当前数据表名称（含前缀）
    //参数绑定
    protected  $bind = [];
    protected $table = '';
    // 查询参数
    protected $options = [];
    // 数据表信息
    protected static $info = [];

    /**
     * 构造函数
     * @access public
     * @param Connection $connection 数据库对象实例
     * @param Model      $model      模型对象
     */
    public function __construct(Connection $connection = null, $model = null)
    {
        $this->connection = $connection ?: TDEngine::connect([], true);

//        $this->prefix     = $this->connection->getConfig('prefix');
//        $this->model      = $model;
        // 设置当前连接的Builder对象
        $this->setBuilder();
    }

    /**
     * 获取当前的数据库Connection对象
     * @access public
     * @return \TDengine\Connection
     */
    public function getConnection()
    {
        return $this->connection;
    }

    /**
     * 切换当前的数据库连接
     * @access public
     * @param mixed $config
     * @return $this
     */
    public function connect($config)
    {
        $this->connection = TDEngine::connect($config);
        $this->setBuilder();

        return $this;
    }
    /**
     * 设置当前的数据库Builder对象
     * @access protected
     * @return void
     */
    protected function setBuilder()
    {
        $class         = $this->connection->getBuilder();
        $this->builder = new $class($this->connection, $this);
    }

    /**
     * 获取当前的builder实例对象
     * @access public
     * @return Builder
     */
    public function getBuilder()
    {
        return $this->builder;
    }

    /**
     * 指定默认的数据表名（不含前缀）
     * @access public
     * @param string $name
     * @return $this
     */
    public function name($name)
    {
        $this->name = $name;
        $this->connection->selectDb($name);
        return $this;
    }


    public function getDb(){
        return $this->connection->getDb();
    }

    /**
     *
     * @DESC:创建表
     * @email:710388898@qq.com
     * @param $table
     * @param array $Schema
     * @param array $tags
     * @param bool $super 是否创建超级表
     * @param bool $using 是否已超级表为模板创建子表
     * @author: 杨同师
     * "TDEngine::name('thingsbase')->createTable('tb51','dev001',$Schema,$tags,true,true);"//创建方式
     * 当是普通表时需要传$Schema，子表时不需要传
     * @Time: 2022/9/15   9:47
     *
     */
    public function createTable($superTable="",$table,$Schema=[],$tags=[],$children=true,$ifNotExists = true){
        $ifNotExistsStr='';
        if ($ifNotExists)
        {
            $ifNotExistsStr= 'IF NOT EXISTS ';
        }
//
//        if($super){
//            $sql = 'CREATE STABLE '.$ifNotExistsStr;
//        }else{
//            $sql = 'CREATE TABLE'.' '.$ifNotExistsStr.' '.$table.' ';
//        }
//
//        if($using){
//            $sql = 'CREATE TABLE'.' '.$ifNotExistsStr.' '.$table.' USING '.$superTable.' ';
//        }else{
//            $sql = 'CREATE TABLE'.' '.$ifNotExistsStr.' '.$table.' ';
//        }
        if(!$children){
            $sql = 'CREATE TABLE'.' '.$ifNotExistsStr.' '.$table.' ';
        }else{
            $sql = 'CREATE TABLE'.' '.$ifNotExistsStr.''.$table.' USING '.$superTable;
        }

        $fields = [];
        if(!empty($Schema)){
            foreach ($Schema as $name=>$type){
                $fields[] =$name.' '.$type;
            }

            $sql .=' (' . implode(',', $fields) . ')';
        }

        $fields = [];
        if(!empty($tags)&&empty($children)){
            foreach ($tags as $name=>$type){
                $fields[] =$name.' '.$type;
            }
            $sql .= ' TAGS (' . implode(',', $fields) . ')';
        }

        if(!empty($tags)&&!empty($children)){
            foreach ($tags as $name=>$type){
                $fields[] =$type;
            }
            $sql .= ' TAGS ('.implode(',', $fields).')';
        }

        return  $this->connection->query($sql);

    }

    /**
     *
     * @DESC:创建超级表
     * @email:710388898@qq.com
     * @param $table
     * @param array $Schema
     * @param array $tags
     * @param bool $ifNotExists
     * @return mixed
     * @author: 杨同师
     * @Time: 2022/12/16   17:32
     *
     */
    public function createStable($table,$Schema=[],$tags=[],$ifNotExists=false){
        try{
            $ifNotExistsStr='';
            if ($ifNotExists)
            {
                $ifNotExistsStr= 'IF NOT EXISTS ';
            }
            $sql = 'CREATE STABLE '.$ifNotExistsStr.$table;

            $fields = [];
            if(!empty($Schema)){
                foreach ($Schema as $name=>$type){
                    $fields[] =$name.' '.$type;
                }

                $sql .=' (' . implode(',', $fields) . ')';
            }
            $fields = [];
            if(!empty($tags)){
                foreach ($tags as $name=>$type){
                    $fields[] =$name.' '.$type;
                }
                $sql .= ' TAGS (' . implode(',', $fields) . ')';
            }
            return  $this->connection->query($sql);
        }catch (\Exception $e){
            echo $e->getMessage().PHP_EOL;
        }

    }



//    public function createTable($superTable="",$table,$Schema=[],$tags=[],$super=true,$ifNotExists = true){
//        if($super){
//            $sql = 'CREATE STABLE ';
//        }else{
//            $sql = 'CREATE TABLE '.$table.' USING '.$superTable.' ';
//        }
//        if ($ifNotExists)
//        {
//            $sql .= 'IF NOT EXISTS';
//        }
//        $fields = [];
//        if(!empty($Schema)&&!empty($super)){
//            foreach ($Schema as $name=>$type){
//                $fields[] =$name.' '.$type;
//            }
//
//            $sql .= $this->name.'.'.$table  . ' (' . implode(',', $fields) . ')';
//        }
//        $fields = [];
//        if(!empty($tags)){
//            foreach ($tags as $name=>$type){
//                if(!empty($super)){
//                    $fields[] =$name.' '.$type;
//                }else{
//                    $fields[]=''.$type;
//                }
//
//            }
//            $sql .= ' TAGS (' . implode(',', $fields) . ')';
//        }
//        print_r($sql);
//        return  $this->connection->query($sql);
//
//    }
    /**
     * 指定默认数据表名（含前缀）
     * @access public
     * @param string $table 表名
     * @return $this
     */
    public function setTable($table)
    {
        $this->table = $table;
        return $this;
    }
    // 获取当前数据表字段类型
    public function getFieldsType($table = '')
    {
        return $this->getTableInfo($table ?: $this->getOptions('table'), 'type');
    }
    // 获取当前数据表绑定信息
    public function getFieldsBind($table = '')
    {
        $types = $this->getFieldsType($table);
        $bind  = [];
        if ($types) {
            foreach ($types as $key => $type) {
                $bind[$key] = $this->getFieldBindType($type);
            }
        }
        return $bind;
    }
    /**
     * 获取字段绑定类型
     * @access public
     * @param string $type 字段类型
     * @return integer
     */
    protected function getFieldBindType($type)
    {
        if (preg_match('/TIMESTAMP/is', $type)) {
            $bind = 1;
        } elseif(preg_match('/FLOAT/is', $type)) {
            $bind = 2;
        }elseif(preg_match('/INT/is', $type)) {
            $bind = 3;
        }elseif(preg_match('/BINARY/is', $type)) {
            $bind = 4;
        }else {
            $bind = 4;
        }
        return $bind;
    }
    /**
     * 参数绑定
     * @access public
     * @param mixed   $key   参数名
     * @param mixed   $value 绑定变量值
     * @param integer $type  绑定类型
     * @return $this
     */
    public function bind($key, $value = false, $type = 4)
    {
        if (is_array($key)) {
            $this->bind = array_merge($this->bind, $key);
        } else {
            $this->bind[$key] = [$value, $type];
        }

        return $this;
    }
    /**
     * 检测参数是否已经绑定
     * @access public
     * @param string $key 参数名
     * @return bool
     */
    public function isBind($key)
    {
        return isset($this->bind[$key]);
    }

    /**
     *
     * @DESC:添加列
     * @email:710388898@qq.com
     * @param string $col_name
     * @param string $column_type
     * @param bool $super 是否修改超级表
     * @return |null
     * @author: 杨同师
     * @Time: 2022/9/15   10:32
     */
    public function addColumn($col_name = '',$column_type='INT',$super=true){
        if(!is_string($col_name)){return null;}
        if(!is_string($column_type)){return null;}
        if($super){
            $sql='ALTER STABLE '.$this->options['table'].' ADD COLUMN '.$col_name.' '.$column_type;
        }else{
            $sql='ALTER TABLE'.' '.$this->options['table'].' ADD COLUMN '.$col_name.' '.$column_type;
        }

        $tableInfo= $this->getSuperTableInfo($this->options['table']);
        $colList=[];
        if(!empty($tableInfo)){
            $colList=array_column($tableInfo,'Field');
        }
        if(in_array($col_name,$colList)){
            throw new \RuntimeException(sprintf('Field already exists', $col_name));
        }
        return  $this->connection->query($sql);
    }

    /**
     *
     * @DESC:删除列
     * @email:710388898@qq.com
     * @param string $col_name
     * @author: 杨同师
     * @Time: 2022/9/15   10:53
     *
     */
    public function dropColumn($col_name = '',$super=true){
        if(!is_string($col_name)){return null;}
        if($super){
            $sql='ALTER STABLE '.$this->options['table'].' DROP COLUMN '.$col_name;
        }else{
            $sql='ALTER TABLE'.' '.$this->options['table'].' DROP COLUMN '.$col_name;
        }

        $tableInfo= $this->getSuperTableInfo($this->options['table']);
        $colList=[];
        if(!empty($tableInfo)){
            $colList=array_column($tableInfo,'Field');
        }
        if(!in_array(strtolower($col_name),$colList)){


            throw new \RuntimeException(sprintf('Field does not exist', $col_name));
        }
        print_r($sql);
        return  $this->connection->query($sql);
    }

    /**
     *
     * @DESC:修改列宽
     * @email:710388898@qq.com
     * @param string $col_name
     * @param string $column_type
     * @param int $length
     * @return null
     * @author: 杨同师
     * @Time: 2022/9/15   10:58
     */
    public function modifyColumn($col_name = '',$column_type='BINARY',$length=0,$super=true){
        if(!is_string($col_name)){return null;}
        if(!is_string($column_type)){return null;}
        $column_type=strtolower($column_type);
        $tableInfo=[];
        if($super){
            $sql='ALTER STABLE '.$this->options['table'].' MODIFY COLUMN '.$col_name.' '.$column_type.'('.$length.')';
        }else{
            $sql='ALTER TABLE'.' '.$this->options['table'].' MODIFY COLUMN '.$col_name.' '.$column_type.'('.$length.')';
        }

        $tableInfo= $this->connection->getFields($this->options['table']);
        $colList=[];
        if(!empty($tableInfo)){
            $colList=array_column($tableInfo,'Field');
        }
        foreach ($tableInfo as $k=>$v){
            if($v['Field']==$col_name&&$length<$v['Length']){
                throw new \RuntimeException(sprintf('The length cannot be less than the original value', $col_name));
            }
        }
        $result=$this->connection->query($sql);
        $this->connection->close();
        return  $result;
    }

    /**
     *
     * @DESC:添加标签
     * @email:710388898@qq.com
     * @param string $tag_name
     * @param string $tag_type
     * @return null
     * @author: 杨同师
     * @Time: 2022/9/15   11:45
     *
     */
    public function addTag($tag_name='',$tag_type='INT'){
        if(!is_string($tag_name)){return null;}
        if(!is_string($tag_type)){return null;}
        $sql='ALTER STABLE '.$this->options['table'].' ADD TAG '.$tag_name.' '.$tag_type;
        $tableInfo= $this->getSuperTableInfo($this->options['table']);
        $colList=[];
        if(!empty($tableInfo)){
            $colList=array_column($tableInfo,'Field');
        }
        if(in_array($tag_name,$colList)){
            foreach ($tableInfo as $k=>$v){
                if($v['Note']=='TAG'){
                    throw new \RuntimeException(sprintf('tag already exists', $tag_name));
                }
            }
        }
        return  $this->connection->query($sql);
    }

    /**
     *
     * @DESC:删除标签
     * @email:710388898@qq.com
     * @param string $tag_name
     * @return |null
     * @author: 杨同师
     * @Time: 2022/9/15   12:08
     *
     */
    public function dropTag($tag_name=''){
        if(!is_string($tag_name)){return null;}
        $sql='ALTER STABLE '.$this->options['table'].' DROP TAG '.$tag_name;
        $tableInfo= $this->getSuperTableInfo($this->options['table']);
        $colList=[];
        if(!empty($tableInfo)){
            $colList=array_column($tableInfo,'Field');
        }

        if(!in_array($tag_name,$colList)){
            throw new \RuntimeException(sprintf('tag not already exists', $tag_name));
        }

        return  $this->connection->query($sql);

    }

    /**
     *
     * @DESC:
     * @email:710388898@qq.com
     * @param string $table_name
     * @param bool $super 是否删除超级表
     * @return |null
     * @author: 杨同师
     * @Time: 2022/12/16   15:17
     *
     */
    public function dropTable($table_name='',$super=true){
        if(empty($table_name)){return null;}
        if(!empty($super)){
            $sql='DROP STABLE '.$table_name;
        }else{
            $sql='DROP TABLE'.' '.$table_name;
        }

        return  $this->connection->query($sql);
    }

    /**
     *
     * @DESC:获取超级表结构信息
     * @email:710388898@qq.com
     * @param string $tableName
     * @author: 杨同师
     * @Time: 2022/9/15   10:42
     *
     */
    public function getSuperTableInfo($tableName = ''){
        if (!$tableName) {
            $tableName = $this->getTable();
        }

       $sql='DESCRIBE '.$tableName;
       $tableInfo= $this->connection->query($sql);
       $this->connection->close();
       return  $tableInfo;

    }

    /**
     * 获取数据表信息
     * @access public
     * @param mixed  $tableName 数据表名 留空自动获取
     * @param string $fetch     获取信息类型 包括 fields type bind pk
     * @return mixed
     */
    public function getTableInfo($tableName = '', $fetch = '')
    {
        if (!$tableName) {
            $tableName = $this->getTable();
        }
        if (is_array($tableName)) {
            $tableName = key($tableName) ?: current($tableName);
        }

        if (strpos($tableName, ',')) {
            // 多表不获取字段信息
            return false;
        } else {
            $tableName = $this->parseSqlTable($tableName);
        }

        // 修正子查询作为表名的问题
        if (strpos($tableName, ')')) {
            return [];
        }

        list($guid) = explode(' ', $tableName);
        $db         = $this->getConfig('database');
        if (!isset(self::$info[$db . '.' . $guid])) {
            if (!strpos($guid, '.')) {
                $schema = $db . '.' . $guid;
            } else {
                $schema = $guid;
            }
            $info = $this->connection->getFields($guid);

            $fields = array_keys($info);
            $bind   = $type   = [];
            foreach ($info as $key => $val) {
                // 记录字段类型
                $type[$key] = $val['Type'];
                $bind[$key] = $this->getFieldBindType($val['Type']);
                if (!empty($val['primary'])) {
                    $pk[] = $key;
                }
            }

            if (isset($pk)) {
                // 设置主键
                $pk = count($pk) > 1 ? $pk : $pk[0];
            } else {
                $pk = null;
            }
            self::$info[$db . '.' . $guid] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk];
        }

        return $fetch ? self::$info[$db . '.' . $guid][$fetch] : self::$info[$db . '.' . $guid];
    }
    /**
     * 执行查询 返回数据集
     * @access public
     * @param string      $sql    sql指令
     * @param array       $bind   参数绑定
     * @param boolean     $master 是否在主服务器读操作
     * @param bool|string $class  指定返回的数据集对象

     */
    public function query($sql, $bind = [], $master = false, $class = false)
    {

        return $this->connection->query($sql);
    }
    /**
     * 得到某个字段的值
     * @access public
     * @param string $field   字段名
     * @param mixed  $default 默认值
     * @param bool   $force   强制转为数字类型
     * @return mixed
     */
    public function value($field, $default = null, $force = false)
    {
        $result = false;
//        if (empty($this->options['fetch_sql']) && !empty($this->options['cache'])) {
//            // 判断查询缓存
//            $cache = $this->options['cache'];
//            if (empty($this->options['table'])) {
//                $this->options['table'] = $this->getTable();
//            }
//            $key    = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind));
//            $result = Cache::get($key);
//        }
        if (false === $result) {
            if (isset($this->options['field'])) {
                unset($this->options['field']);
            }
            $pdo = $this->field($field)->limit(1)->getTDO();
            if (is_string($pdo)) {
                // 返回SQL语句
                return $pdo;
            }

            $result = $pdo->fetchColumn();
            if ($force) {
                $result = (float) $result;
            }


        } else {
            // 清空查询条件
            $this->options = [];
        }
        return false !== $result ? $result : $default;
    }
    /**
     * 指定查询字段 支持字段排除和指定数据表
     * @access public
     * @param mixed   $field
     * @param boolean $except    是否排除
     * @param string  $tableName 数据表名
     * @param string  $prefix    字段前缀
     * @param string  $alias     别名前缀
     * @return $this
     */
    public function field($field, $except = false, $tableName = '', $prefix = '', $alias = '')
    {

        if (empty($field)) {
            return $this;
        } elseif ($field instanceof Expression) {
            $this->options['field'][] = $field;
            return $this;
        }

        if (is_string($field)) {
//            if (preg_match('/[\<\'\"\(]/', $field)) {
//                return $this->fieldRaw($field);
//            }
            $field = array_map('trim', explode(',', $field));

        }
        if (true === $field) {
            // 获取全部字段
            $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields');
            $field  = $fields ?: ['*'];

        } elseif ($except) {
            // 字段排除
            $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields');
            $field  = $fields ? array_diff($fields, $field) : $field;

        }
        if ($tableName) {
            // 添加统一的前缀
            $prefix = $prefix ?: $tableName;
            foreach ($field as $key => $val) {
                if (is_numeric($key)) {
                    $val = $prefix . '.' . $val . ($alias ? ' AS ' . $alias . $val : '');
                }
                $field[$key] = $val;
            }
        }

        if (isset($this->options['field'])) {
            $field = array_merge((array) $this->options['field'], $field);
        }

        $this->options['field'] = array_unique($field);
        return $this;
    }

    /**
     *
     * @DESC:设置超级表
     * @email:710388898@qq.com
     * @param $super 超级表名称
     * @author: 杨同师
     * @Time: 2022/9/13   11:02
     *
     */
    public function using(string $super=''){
        if(empty($super)){
            return $this;
        }
        if(is_string($super)){
            $this->options['using']=$super;
            return $this;
        }
    }
    /**
     *
     * @DESC:设置Tags
     * @email:710388898@qq.com
     * @param array $tags
     * @return $this
     * @author: 杨同师
     * @Time: 2022/9/13   11:31
     *
     */
    public function tag(array $tags=[]){


        if(is_array($tags)){
            $this->options['tags']=$tags;
            return  $this;
        }
    }

    /**
     *
     * @DESC:求和计算
     * @email:710388898@qq.com
     * @param string $sum
     * @return $this
     * @author: 杨同师
     * @Time: 2022/12/16   15:49
     *
     */
    public function sum($sum=''){
        $this->options['field'].=$sum;
        return $this;
    }

    /**
     * 使用表达式设置数据
     * @access public
     * @param  mixed $value 表达式
     * @return Expression
     */
    public function raw($value)
    {
        return new Expression($value);
    }
    /**
     * 指定表达式查询条件
     * @access public
     * @param  string $where  查询条件
     * @param  array  $bind   参数绑定
     * @param  string $logic  查询逻辑 and or xor
     * @return $this
     */
    public function whereRaw($where, $bind = [], $logic = 'AND')
    {
        $this->options['where'][$logic][] = $this->raw($where);

        if ($bind) {
            $this->bind($bind);
        }

        return $this;
    }
    /**
     * 指定AND查询条件
     * @access public
     * @param mixed $field     查询字段
     * @param mixed $op        查询表达式
     * @param mixed $condition 查询条件
     * @return $this
     */
    public function where($field, $op = null, $condition = null)
    {
        $param = func_get_args();
        array_shift($param);
        $this->parseWhereExp('AND', $field, $op, $condition, $param);

        return $this;
    }
    /**
     * 指定In查询条件
     * @access public
     * @param mixed  $field     查询字段
     * @param mixed  $condition 查询条件
     * @param string $logic     查询逻辑 and or xor
     * @return $this
     */
    public function whereIn($field, $condition, $logic = 'AND')
    {
        $this->parseWhereExp($logic, $field, 'in', $condition, [], true);
        return $this;
    }

    /**
     * 指定Between查询条件
     * @access public
     * @param mixed  $field     查询字段
     * @param mixed  $condition 查询条件
     * @param string $logic     查询逻辑 and or xor
     * @return $this
     */
    public function whereBetween($field, $condition, $logic = 'AND')
    {
        $this->parseWhereExp($logic, $field, 'between', $condition, [], true);
        return $this;
    }

    /**
     * 指定OR查询条件
     * @access public
     * @param mixed $field     查询字段
     * @param mixed $op        查询表达式
     * @param mixed $condition 查询条件
     * @return $this
     */
    public function whereOr($field, $op = null, $condition = null)
    {
        $param = func_get_args();
        array_shift($param);
        $this->parseWhereExp('OR', $field, $op, $condition, $param);
        return $this;
    }
    /**
     * 指定group查询
     * @access public
     * @param string $group GROUP
     * @return $this
     */
    public function group($group)
    {
        $this->options['group'] = $group;
        return $this;
    }

    /**
     * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc'])
     * @access public
     * @param string|array $field 排序字段
     * @param string       $order 排序
     * @return $this
     */
    public function order($field, $order = null)
    {
        if (empty($field)) {
            return $this;
        } elseif ($field instanceof Expression) {
            $this->options['order'][] = $field;
            return $this;
        }

        if (is_string($field)) {
            if (!empty($this->options['via'])) {
                $field = $this->options['via'] . '.' . $field;
            }
            if (strpos($field, ',')) {
                $field = array_map('trim', explode(',', $field));
            } else {
                $field = empty($order) ? $field : [$field => $order];
            }
        } elseif (!empty($this->options['via'])) {
            foreach ($field as $key => $val) {
                if (is_numeric($key)) {
                    $field[$key] = $this->options['via'] . '.' . $val;
                } else {
                    $field[$this->options['via'] . '.' . $key] = $val;
                    unset($field[$key]);
                }
            }
        }
        if (!isset($this->options['order'])) {
            $this->options['order'] = [];
        }
        if (is_array($field)) {
            $this->options['order'] = array_merge($this->options['order'], $field);
        } else {
            $this->options['order'][] = $field;
        }

        return $this;
    }
    /**
     * 指定查询数量
     * @access public
     * @param mixed $offset 起始位置
     * @param mixed $length 查询数量
     * @return $this
     */
    public function limit($offset, $length = null)
    {
        if (is_null($length) && strpos($offset, ',')) {
            list($offset, $length) = explode(',', $offset);
        }
        $this->options['limit'] = intval($offset) . ($length ? ',' . intval($length) : '');
        return $this;
    }

    /**
     *
     * @DESC:聚合
     * @email:710388898@qq.com
     * @param $offset
     * @param null $length
     * @return $this
     * @author: 杨同师
     * @Time: 2022/12/19   16:24
     *参数
     */
    public function interval($interval)
    {
        if (is_null($interval)) {
            return  false;
        }
        $this->options['interval'] = $interval;
        return $this;
    }

    /**
     *
     * @DESC:填充模式
     * @email:710388898@qq.com
     * @param $fill
     * @return $this|bool
     * @author: 杨同师
     * @Time: 2023/1/17   19:58
     *不进行填充：NONE（默认填充模式）。
                VALUE 填充：固定值填充，此时需要指定填充的数值。例如：FILL(VALUE, 1.23)。
                PREV 填充：使用前一个非 NULL 值填充数据。例如：FILL(PREV)。
                NULL 填充：使用 NULL 填充数据。例如：FILL(NULL)。
                LINEAR 填充：根据前后距离最近的非 NULL 值做线性插值填充。例如：FILL(LINEAR)。
                NEXT 填充：使用下一个非 NULL 值填充数据。例如：FILL(NEXT)。
     */
    public function fill($fill){
        if (is_null($fill)) {
            return  false;
        }
        $this->options['fill'] = $fill;
        return $this;
    }
    /**
     * 指定分页
     * @access public
     * @param mixed $page     页数
     * @param mixed $listRows 每页数量
     * @return $this
     */
    public function page($page, $listRows = null)
    {
        if (is_null($listRows) && strpos($page, ',')) {
            list($page, $listRows) = explode(',', $page);
        }
        $this->options['page'] = [intval($page), intval($listRows)];
        return $this;
    }
    /**
     * 执行查询但只返回PDOStatement对象
     * @access public
     * @return \PDOStatement|string
     */
    public function getTDO()
    {
        // 分析查询表达式
        $options = $this->parseExpress();
        // 生成查询SQL
        $sql = $this->builder->select($options);

        // 执行查询操作
        return $this->query($sql);
    }
    /**
     * 插入记录
     * @access public
     * @param mixed   $data         数据
     * @param boolean $replace      是否replace
     * @param boolean $getLastInsID 返回自增主键
     * @param string  $sequence     自增序列名
     * @return integer|string
     */
    public function insert(array $data = [], $auto=false)
    {
        $replace = false;
        // 分析查询表达式
        $options = $this->parseExpress();
        $data    = array_merge($options['data'], $data);

        // 生成SQL语句
        $sql = $this->builder->insert($data, $options, $replace,$auto);

        // 获取参数绑定
        $bind = $this->getBind();
        try{
            if ($options['fetch_sql']) {
                // 获取实际执行的SQL语句
                return $this->connection->query($sql);
            }
            // 执行操作
            $result = 0 === $sql ? 0 : $this->query($sql);

            return $result;
        }catch (\Exception $e){
            return  0;
        }
    }
    /**
     * 批量插入记录
     * @access public
     * @param mixed     $dataSet 数据集
     * @param boolean   $replace  是否replace
     * @param integer   $limit   每次写入数据限制
     * @return integer|string
     */
    public function insertAll(array $dataSet,  $limit = null)
    {
        // 分析查询表达式
        $options = $this->parseExpress();
        if (!is_array(reset($dataSet))) {
            return false;
        }
        // 生成SQL语句
        if (is_null($limit)) {
            $sql = $this->builder->insertAll($dataSet, $options);
        } else {
            $array = array_chunk($dataSet, $limit, true);
            foreach ($array as $item) {
                $sql[] = $this->builder->insertAll($item, $options);
            }
        }
        // 获取参数绑定
        $bind = $this->getBind();
        if ($options['fetch_sql']) {
            // 获取实际执行的SQL语句
            return $this->connection->getRealSql($sql, $bind);
        } elseif (is_array($sql)) {
            // 执行操作
            return $this->query($sql);
        } else {
            // 执行操作
            return $this->query($sql);
        }
    }

    /**
     * 获取绑定的参数 并清空
     * @access public
     * @return array
     */
    public function getBind()
    {
        $bind       = $this->bind;
        $this->bind = [];
        return $bind;
    }
    /**
     * 创建子查询SQL
     * @access public
     * @param bool $sub
     * @return string
     * @throws DbException
     */
    public function buildSql($sub = true)
    {
        return $sub ? '( ' . $this->select(false) . ' )' : $this->select(false);
    }
    /**
     * 查找记录
     * @access public
     * @param array|string|Query|\Closure $data
     * @return Collection|false|\PDOStatement|string
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     */
    public function select($data = null)
    {
        if ($data instanceof Query) {
            return $data->select();
        } elseif ($data instanceof \Closure) {
            call_user_func_array($data, [ & $this]);
            $data = null;
        }

        // 分析查询表达式
        $options = $this->parseExpress();

        // 获取参数绑定
        $bind = $this->getBind();

        $resultSet = false;
        if (false === $resultSet) {
            // 生成查询SQL

            $sql = $this->builder->select($options);
            
           
//            // 获取参数绑定
//            $bind = $this->getBind();
            if ($options['fetch_sql']) {
                // 获取实际执行的SQL语句
                return $this->connection->getRealSql($sql, $bind);
            }
            $options['data'] = $data;

            $resultSet = $this->connection->query($sql);
        }
        return $resultSet;
    }

    /**
     *
     * @DESC:查询单个记录
     * @email:710388898@qq.com
     * @param null $data
     * @return bool|false|\PDOStatement|string|Collection
     * @author: 杨同师
     * @Time: 2022/9/14   18:23
     *
     */
    public function find($data = null)
    {
        if ($data instanceof Query) {
            return $data->select();
        } elseif ($data instanceof \Closure) {
            call_user_func_array($data, [ & $this]);
            $data = null;
        }
        // 分析查询表达式
        $options = $this->parseExpress();

        // 获取参数绑定
        $bind = $this->getBind();
        $result           = false;
        $resultSet = false;
        if (false === $result) {
            // 生成查询SQL
            $sql = $this->builder->select($options);
            //dump($sql);
//            // 获取参数绑定
//            $bind = $this->getBind();
            if ($options['fetch_sql']) {
                // 获取实际执行的SQL语句
                return $this->connection->getRealSql($sql, $bind);
            }
            $options['data'] = $data;

            $resultSet = $this->connection->query($sql);
            $result = isset($resultSet[0]) ? $resultSet[0] : null;
        }
        return $result;
    }
    public function fetch(){
        return $this->connection->fetch();
    }
    /**
     * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名（小写）
     * @access public
     * @param string $sql sql语句
     * @return string
     */
    public function parseSqlTable($sql)
    {
       /*拼接带前缀的数据表，目前没有，用不到*/
        if (false !== strpos($sql, '__')) {
            $prefix = $this->prefix;
            $sql    = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) {
                return $prefix . strtolower($match[1]);
            }, $sql);
        }
        return $sql;
    }

    /**
     * 得到当前或者指定名称的数据表
     * @access public
     * @param string $name
     * @return string
     */
    public function getTable($name = '')
    {
        if ($name || empty($this->table)) {
            $name      = $name ?: $this->name;
            $tableName =$name;

        } else {
            $tableName = $this->table;
        }
        return $tableName;
    }
    /**
     * 获取当前的查询参数
     * @access public
     * @param string $name 参数名
     * @return mixed
     */
    public function getOptions($name = '')
    {
        if ('' === $name) {
            return $this->options;
        } else {
            return isset($this->options[$name]) ? $this->options[$name] : null;
        }
    }
    /**
     * 指定数据表别名
     * @access public
     * @param mixed $alias 数据表别名
     * @return $this
     */
    public function alias($alias)
    {
        if (is_array($alias)) {
            foreach ($alias as $key => $val) {
                if (false !== strpos($key, '__')) {
                    $table = $this->parseSqlTable($key);
                } else {
                    $table = $key;
                }
                $this->options['alias'][$table] = $val;
            }
        } else {
            if (isset($this->options['table'])) {
                $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table'];
                if (false !== strpos($table, '__')) {
                    $table = $this->parseSqlTable($table);
                }
            } else {

                $table = $this->getTable();
            }

            $this->options['alias'][$table] = $alias;
        }
        return $this;
    }
    /**
     * 指定当前操作的数据表
     * @access public
     * @param mixed $table 表名
     * @return $this
     */
    public function table($table)
    {
        if (is_string($table)) {
            if (strpos($table, ')')) {
                // 子查询
            } elseif (strpos($table, ',')) {
                $tables = explode(',', $table);
                $table  = [];
                foreach ($tables as $item) {
                    list($item, $alias) = explode(' ', trim($item));
                    if ($alias) {
                        $this->alias([$item => $alias]);
                        $table[$item] = $alias;
                    } else {
                        $table[] = $item;
                    }
                }
            } elseif (strpos($table, ' ')) {
                list($table, $alias) = explode(' ', $table);

                $table = [$table => $alias];
                $this->alias($table);
            }
        } else {
            $tables = $table;
            $table  = [];
            foreach ($tables as $key => $val) {
                if (is_numeric($key)) {
                    $table[] = $val;
                } else {
                    $this->alias([$key => $val]);
                    $table[$key] = $val;
                }
            }
        }

        $this->options['table'] = $table;
        return $this;
    }
    /**
     * 分析查询表达式
     * @access public
     * @param string                $logic     查询逻辑 and or xor
     * @param string|array|\Closure $field     查询字段
     * @param mixed                 $op        查询表达式
     * @param mixed                 $condition 查询条件
     * @param array                 $param     查询参数
     * @param  bool                 $strict    严格模式
     * @return void
     */
    protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false)
    {
        $logic = strtoupper($logic);
        if ($field instanceof \Closure) {
            $this->options['where'][$logic][] = is_string($op) ? [$op, $field] : $field;
            return;
        }

        if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) {
            $field = $this->options['via'] . '.' . $field;
        }

        if ($field instanceof Expression) {

            return $this->whereRaw($field, is_array($op) ? $op : []);
        } elseif ($strict) {
            // 使用严格模式查询
            $where[$field] = [$op, $condition];

            // 记录一个字段多次查询条件
            $this->options['multi'][$logic][$field][] = $where[$field];
        } elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {

            $where[] = ['exp', $this->raw($field)];
            if (is_array($op)) {
                // 参数绑定
                $this->bind($op);
            }
        } elseif (is_null($op) && is_null($condition)) {
            if (is_array($field)) {
                // 数组批量查询
                $where = $field;
                foreach ($where as $k => $val) {
                    $this->options['multi'][$logic][$k][] = $val;
                }

            } elseif ($field && is_string($field)) {
                // 字符串查询
                $where[$field]                            = ['null', ''];
                $this->options['multi'][$logic][$field][] = $where[$field];
            }
        } elseif (is_array($op)) {

            $where[$field] = $param;
        } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) {

            // null查询
            $where[$field] = [$op, ''];

            $this->options['multi'][$logic][$field][] = $where[$field];
        } elseif (is_null($condition)) {
            // 字段相等查询
            $where[$field] = ['eq', $op];

            $this->options['multi'][$logic][$field][] = $where[$field];
        } else {

            if ('exp' == strtolower($op)) {
                $where[$field] = ['exp', $this->raw($condition)];
                // 参数绑定
                if (isset($param[2]) && is_array($param[2])) {
                    $this->bind($param[2]);
                }
            } else {
                $where[$field] = [$op, $condition];
            }
            // 记录一个字段多次查询条件
            $this->options['multi'][$logic][$field][] = $where[$field];
        }

        if (!empty($where)) {

            if (!isset($this->options['where'][$logic])) {
                $this->options['where'][$logic] = [];
            }
            if (is_string($field) && $this->checkMultiField($field, $logic)) {
                $where[$field] = $this->options['multi'][$logic][$field];
            } elseif (is_array($field)) {
                foreach ($field as $key => $val) {
                    if ($this->checkMultiField($key, $logic)) {
                        $where[$key] = $this->options['multi'][$logic][$key];
                    }
                }
            }

            $this->options['where'][$logic] = array_merge($this->options['where'][$logic], $where);
        }
    }

    /**
     * 检查是否存在一个字段多次查询条件
     * @access public
     * @param string $field 查询字段
     * @param string $logic 查询逻辑 and or xor
     * @return bool
     */
    private function checkMultiField($field, $logic)
    {
        return isset($this->options['multi'][$logic][$field]) && count($this->options['multi'][$logic][$field]) > 1;
    }
    /**
     * 分析表达式（可用于查询或者写入操作）
     * @access protected
     * @return array
     */
    protected function parseExpress()
    {
        $options = $this->options;

        // 获取数据表
        if (empty($options['table'])) {
            $options['table'] = $this->getTable();
        }

        if (!isset($options['where'])) {
            $options['where'] = [];
        } elseif (isset($options['view'])) {
            // 视图查询条件处理
            foreach (['AND', 'OR'] as $logic) {
                if (isset($options['where'][$logic])) {
                    foreach ($options['where'][$logic] as $key => $val) {
                        if (array_key_exists($key, $options['map'])) {
                            $options['where'][$logic][$options['map'][$key]] = $val;
                            unset($options['where'][$logic][$key]);
                        }
                    }
                }
            }

            if (isset($options['order'])) {
                // 视图查询排序处理
                if (is_string($options['order'])) {
                    $options['order'] = explode(',', $options['order']);
                }
                foreach ($options['order'] as $key => $val) {
                    if (is_numeric($key)) {
                        if (strpos($val, ' ')) {
                            list($field, $sort) = explode(' ', $val);
                            if (array_key_exists($field, $options['map'])) {
                                $options['order'][$options['map'][$field]] = $sort;
                                unset($options['order'][$key]);
                            }
                        } elseif (array_key_exists($val, $options['map'])) {
                            $options['order'][$options['map'][$val]] = 'asc';
                            unset($options['order'][$key]);
                        }
                    } elseif (array_key_exists($key, $options['map'])) {
                        $options['order'][$options['map'][$key]] = $val;
                        unset($options['order'][$key]);
                    }
                }
            }
        }

        if (!isset($options['field'])) {
            $options['field'] = '*';
        }

        if (!isset($options['data'])) {
            $options['data'] = [];
        }



        foreach (['master', 'lock', 'fetch_pdo', 'fetch_sql', 'distinct'] as $name) {
            if (!isset($options[$name])) {
                $options[$name] = false;
            }
        }


        foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment','interval','fill'] as $name) {
            if (!isset($options[$name])) {
                $options[$name] = '';
            }
        }

        if (isset($options['page'])) {
            // 根据页数计算limit
            list($page, $listRows) = $options['page'];
            $page                  = $page > 0 ? $page : 1;
            $listRows              = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 10000);
            $offset                = $listRows * ($page - 1);
            $options['limit']      = $offset . ',' . $listRows;
        }

        $this->options = [];

        return $options;
    }
    /**
     * 获取数据库的配置参数
     * @access public
     * @param string $name 参数名称
     * @return boolean
     */
    public function getConfig($name = '')
    {
        return $this->connection->getConfig($name);
    }
}